package divestoclimb.checklist;

import java.text.DateFormat;
import java.text.ParseException;

import divestoclimb.checklist.data.Check;
import divestoclimb.checklist.storage.DbAdapter;
import divestoclimb.util.FriendlyDate;
import android.content.Context;
import android.database.Cursor;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

/**
 * An extension of CursorAdapter that handles binding a Cursor of Items, some of
 * which are Checks and others are Notes. This adapter inflates the proper view for
 * both and handles manipulating the base view to reflect special cases such as
 * drag bars and note text fields.
 * @author BenR
 */
public class ItemCursorAdapter extends CursorAdapter {
	
	private boolean mIsEditable;
	private int mCheckLayout, mNoteLayout;
	private LayoutInflater mInflater;
	protected int mFrom[];
	protected int mTo[];
	private String mTypeIndex;
	protected Context myContext;
	protected DateFormat mDateFormat;

	public static final boolean LIST_TYPE_TEMPLATE=false;
	public static final boolean LIST_TYPE_CHECKLIST=true;
	
	public static final int VIEW_TYPE_DEFAULT=-1;
	public static final int VIEW_TYPE_STRING=1;
	public static final int VIEW_TYPE_DATE=2;
	
	// mTypes is an array of int's that represent the type of information
	// to put in this view: VIEW_TYPE_DEFAULT, VIEW_TYPE_STRING, VIEW_TYPE_DATE 
	public int mTypes[];
	
	/**
	 * ViewTag is simply a container to hold all the information we need to keep
	 * about our Views. It borrows heavily from
	 * SimpleCursorAdapter.generateViewHolder.
	 * @author BenR
	 */
	private class ViewTag {
		public View mHolder[];
		public int mItemType;

		public ViewTag(View v, int[] to, int item_type) {
			mItemType = item_type;
			rebuild(v, to);
		}
		
		/**
		 * rebuild the ViewTag's array of cached subviews. This must be called
		 * after the tag is copied from one View to another.
		 * @param v The parent View of this tag
		 * @param to An array of view ID's to look up and cache
		 */
		public void rebuild(View v, int[] to) {
			final int count = to.length;
			mHolder = new View[count];
			for(int i = 0; i < count; i++) {
				mHolder[i] = v.findViewById(to[i]);
			}
		}
	}
	
	/**
	 * Constructor. Similar arguments as SimpleCursorAdapter. Accepts two layouts:
	 * one for notes and the other for checks.
	 * 
	 * @param context The current Context
	 * @param checkLayout The layout to use for Check items
	 * @param noteLayout The layout to use for Note items
	 * @param c The Cursor to bind to
	 * @param from An array of strings of column names to bind from the Cursor
	 * @param to An array of View ID's to bind the column name of the corresponding index. If the ID does not exist in one view or the other that's okay, as long as there's no corresponding data to go in that view.
	 * @param types an Array of constants to define how to interpret each column
	 * @param date_format A DateFormat object to use to parse dates. This can be null if no date fields are defined.
	 * @param typeIndex The column index that identifies the item type
	 * @param editable Whether or not these Items are being loaded into an interface where the items themselves can be modified.
	 */
	public ItemCursorAdapter(Context context, int checkLayout, int noteLayout, Cursor c, DateFormat date_format, String[] from, int[] to, int[] types, String typeIndex, boolean editable) {
		super(context, c);
		mIsEditable=editable;
		mCheckLayout=checkLayout;
		mNoteLayout=noteLayout;
		mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		myContext=context;
		mTypeIndex=typeIndex;
		mTypes=types;
		mDateFormat=date_format;
		mFrom=new int[from.length];
		for(int i=0; i<from.length; i++) {
			mFrom[i]=c.getColumnIndexOrThrow(from[i]);
		}
		mTo=to;
	}

	/**
	 * Inflates view(s) from the specified XML file.
	 * 
	 * @see android.widget.CursorAdapter#newView(android.content.Context,
	 *      android.database.Cursor, ViewGroup)
	 */
	@Override
	public View newView(Context context, Cursor cursor, ViewGroup parent) {
		int layout;
		// Pick the right layout based on type.
		int type=cursor.getInt(cursor.getColumnIndexOrThrow(mTypeIndex));
		if(type == DbAdapter.ITEM_TYPE_CHECK) {
			layout=mCheckLayout;
		} else {
			layout=mNoteLayout;
		}

		View v = mInflater.inflate(layout, parent, false);

		// If we are able to edit the contents of this list, display the move
		// handle.
		if(mIsEditable) {
			ImageView move_handle = (ImageView)v.findViewById(R.id.move_handle);
			move_handle.setVisibility(View.VISIBLE);
			move_handle.setImageResource(R.drawable.ic_mp_move);
		}

		// Inspired by SimpleCursorAdapter.generateViewHolder. Set the view's
		// tag to a new ViewTag object containing all subviews and the type. We
		// need this when binding the cursor to the view.
		v.setTag(new ViewTag(v, mTo, type));

		return v;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		Cursor cursor;
		if((cursor=(Cursor)getItem(position)) == null) {
			throw new IllegalStateException("couldn't move cursor to position " + position);
		}
		// Look up the item to show and see if convertView's type is the same as
		// the type for this item. If so, return convertView. If not, make a new
		// view.
		View v;
		// BR 20091114: Trying to re-use convertView causes dates and other
		// fields to get set on more than one View in the list after it's
		// initially built. I probably am not calling rebuild() on the ViewTag
		// at all the right times.
		/*if((convertView != null) &&
				(cursor.getInt(cursor.getColumnIndexOrThrow(mTypeIndex))
						== ((ViewTag)convertView.getTag()).mItemType)) {
			v = convertView;
			((ViewTag)v.getTag()).rebuild(v, mTo);
		} else {*/
			v = newView(myContext, cursor, parent);
		//}
		bindView(v, myContext, cursor);
		return v;
	}

	/* (non-Javadoc)
	 * @see android.widget.CursorAdapter#bindView(android.view.View, android.content.Context, android.database.Cursor)
	 */
	@Override
	public void bindView(View view, Context context, Cursor cursor) {
		final ViewTag tag = (ViewTag)view.getTag();
		final int count = mTo.length;
		final int[] from = mFrom;
		
		for(int i = 0; i < count; i++) {
			final View v = tag.mHolder[i];
			if(v != null) {
				String text = cursor.getString(from[i]);
				if(text == null) {
					text = "";
				}
				
				// CheckBox is an instance of TextView so we have to check for
				// a CheckBox first
				if(! mIsEditable) {
					// If this is the check box, set its state
					if(v instanceof ImageView) {
						int checkimage = cursor.getInt(from[i]) == Check.STATE_CHECKED? R.drawable.btn_check_on: R.drawable.btn_check_off;
						((ImageView)v).setImageResource(checkimage);
						continue;
					}
					// If this is the item overlay, show it if the check has been
					// skipped
					if(v instanceof LinearLayout) {
						if(cursor.getInt(from[i]) == Check.STATE_SKIPPED) {
							v.setVisibility(0);
						}
						continue;
					}
				}
				
				if(v instanceof TextView) {
					switch(mTypes[i]) {
					case VIEW_TYPE_DATE:
						// This is for setting a time that a field changed
						String formatted_date;
						if(text != "") {
							try {
								formatted_date = FriendlyDate.format(mDateFormat.parse(text));
							} catch(ParseException e) {
								formatted_date = text;
							}
							((TextView)v).setText(formatted_date);
						}
						break;
					case VIEW_TYPE_DEFAULT:
					case VIEW_TYPE_STRING:
					default:
						// This is for setting the text of a check item
						((TextView)v).setText(text);
					}
					continue;
				}
				
				// This is for setting the text of a note
				if(v instanceof ViewGroup) {
					// Big TODO: handle rendering more than one subview including
					// EditTexts. For now, just dump the entire string contents
					// into a single TextView.
					((ViewGroup)v).removeAllViews();
					TextView tv = new TextView(context);
					tv.setText(cursor.getString(from[i]));
					((ViewGroup)v).addView(tv);
				}
			}
		}
	}

}
